#!/usr/bin/env python3

__author__ = 'z.Wick.Tone.Burst'
__doc__ = """ Regression Algorithms, 
    basically LinearRegression 
    and LogisticRegression.
"""


import math
import numpy as np
import random

import zt.ML.utils as mltls



class LinearRegression:
    def __init__(self, D):
        X = np.mat(D.X)
        Y = np.mat(D.Y)
        self.W = ((X.T * X).I * X.T * Y.T).A1

    @property
    def dim(self):
        return self.W.size

    def __call__(self):
        return self.W



class Perceptron:
    pass



class Pocket:
    pass



class LogisticRegression:
    def __init__(self, w0, D):
        self.D = D
        self.W = LinearRegression(D)(); self.W[0] = w0
        self.dim = self.W.size


    def update(self, rounds):
        for i in range(rounds):
            theta = lambda t: 1. / (1 + math.exp(t))
            fn = lambda x, y: theta(-y * self.W.dot(x)) * (-y * x)

            ### Raw Gradient Descent
            # dt = np.array(tuple(map(fn, self.D.X, self.D.Y)))
            # delta = dt.sum(axis=0) / self.D.size
            ### Stochastic Gradient Descent
            x, y = self.D.choice()
            delta = theta(-y * self.W.dot(x)) * (-y * x)

            # eta = math.sqrt(np.power(delta, 2).sum())
            eta = np.power(delta, 2).sum()
            if eta == 0:
                return self.W
            # self.W -= eta * np.array(delta)
            self.W += eta * np.array(delta)
        return self.W





def test_perceptron():
    pass


def test_pocket():
    pass


def test_linear_regress():
    D = mltls.MLData('./data/pla_train.txt')
    A = LinearRegression(D)
    print(A.W)


def test_logistic_regerss():
    D = mltls.MLData('./data/pla_train.txt')
    A = LogisticRegression(0, D)
    A.update(1000)
    print(A.W)
    print(A.W.dot(D.X[1]))



if __name__ == '__main__':
    # test_linear_regress()
    # test_perceptron()
    # test_pocket()
    test_logistic_regerss()
